package com.gitee.ly.cloundgateway;

import com.gitee.ly.DiscoverAndLoadBalancer;
import com.gitee.ly.LightRegistryProperties;
import com.gitee.ly.model.ServiceInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;

@Slf4j
public class LightRegistryGlobalFilter implements GlobalFilter, Ordered {
    private static final int LB_GLOBAL_FILTER_ORDER = RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1;
    private BeanFactory beanFactory;
    private DiscoverAndLoadBalancer discoverAndLoadBalancer;
    private LightRegistryProperties lightRegistryProperties;

    public LightRegistryGlobalFilter(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        this.discoverAndLoadBalancer = this.beanFactory.getBean(DiscoverAndLoadBalancer.class);
        this.lightRegistryProperties = this.beanFactory.getBean(LightRegistryProperties.class);
    }

    @Override
    public int getOrder() {
        return LB_GLOBAL_FILTER_ORDER;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI oldUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        if (oldUri != null && ("lb".equals(oldUri.getScheme())) && this.lightRegistryProperties.isEnable()) {
            String serviceName = oldUri.getHost();
            ServiceInfo serviceInfo = this.discoverAndLoadBalancer.chooseOne(serviceName);
            if (serviceInfo == null) {
                throw NotFoundException.create(false, String.format("Light-Registry serviceName：[%s] NO Find Provider", serviceName));
            }
            boolean encoded = containsEncodedParts(oldUri);
            URI newUri = UriComponentsBuilder.fromUri(oldUri)
                    .scheme(serviceInfo.getSchema())
                    .host(serviceInfo.getIp())
                    .port(serviceInfo.getPort())
                    .build(encoded)
                    .toUri();
            log.info(String.format("Light-Registry serviceLoadBalance：[%s] (transfer to ->) %s ", oldUri, newUri));
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newUri);
            return chain.filter(exchange);
        } else {
            return chain.filter(exchange);
        }
    }

    private static boolean containsEncodedParts(URI uri) {
        boolean encoded = uri.getRawQuery() != null && uri.getRawQuery().contains("%") || uri.getRawPath() != null && uri.getRawPath().contains("%") || uri.getRawFragment() != null && uri.getRawFragment().contains("%");
        if (encoded) {
            try {
                UriComponentsBuilder.fromUri(uri).build(true);
                return true;
            } catch (IllegalArgumentException var3) {
                return false;
            }
        } else {
            return false;
        }
    }

}
